package com.informatics.polymer.server.Polymerisation;

import java.io.IOException;
import java.util.ArrayList;

import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.ParsingException;
import nu.xom.ValidityException;

import org.xmlcml.cml.element.CMLAtom;
import org.xmlcml.cml.element.CMLBond;
import org.xmlcml.cml.element.CMLBondArray;
import org.xmlcml.cml.element.CMLFragment;
import org.xmlcml.cml.element.CMLJoin;
import org.xmlcml.cml.element.CMLMolecule;

/**
 * Class to build block copolymers.<br>
 * </br> <b>Example</b><br>
 * </br> BlockCopolymerisation blockCopolymerisation = new
 * BlockCopolymerisation(
 * 5,2,45.0,"Cl[R]","[R]c1cccc(C[R])c1","[R]OCC1CCCC(C[R])C1","CCO[R]");<br>
 * </br> String result = blockCopolymerisation.getPolymer().toXML();<br>
 * </br> blockCopolymerisation.add3DCoordinates(result);<br>
 * </br> <b>Note</b><br>
 * </br> Coordinate generation is done using external dependencies, from the
 * Chemistry Development kit<br>
 * </br> <a href="http://pele.farmbio.uu.se/nightly/api/org/openscience/cdk/modeling/builder3d/ModelBuilder3D.html"
 * >ModelBuilder3D class</a>.
 * 
 * @author Ed Cannon
 * @version 1.0
 */
public class BlockCopolymerisation implements Polymerisation {
	/**
	 * CMLMolecule representation of the block copolymer.
	 */
	private CMLMolecule blockCopolymerMolecule;
	/**
	 * int representing the degree of polymerisation of block A.
	 */
	private int degreeOfPolymerisationBlockA;
	/**
	 * int representing the degree of polymerisation of block B.
	 */
	private int degreeOfPolymerisationBlockB;
	/**
	 * double representing the torsion angle between repeat units.
	 */
	private double torsionAngle;
	/**
	 * CMLMolecule representation of end group 1.
	 */
	private CMLMolecule endGroup1;
	/**
	 * CMLMolecule representation of block A.
	 */
	private CMLMolecule blockA;
	/**
	 * CMLMolecule representation of block B.
	 */
	private CMLMolecule blockB;
	/**
	 * CMLMolecule representation of end group 2.
	 */
	private CMLMolecule endGroup2;
	/**
	 * Maximum torsion angle allowed.
	 */
	private static final int TORSION_ANGLE_UPPER_LIMIT = 360;
	/**
	 * Integer index for end group 1 id, when combining the separate
	 * CMLMolecules.
	 */
	private static final int END_GROUP1_INDEX = 0;
	/**
	 * Integer index for repeat unit A id to be attached to end group 1.
	 */
	private static final int REPEAT_UNITA_INDEX_START = 1;
	/**
	 * Integer index for repeat unit A id to be attached to repeat unit B.
	 */
	private static final int REPEAT_UNITA_INDEX_END = 2;
	/**
	 * Integer index for repeat unit B id to be attached to repeat unit A.
	 */
	private static final int REPEAT_UNITB_INDEX_START = 3;
	/**
	 * Integer index for repeat unit B id to be attached to end group 2.
	 */
	private static final int REPEAT_UNITB_INDEX_END = 4;
	/**
	 * Integer index for end group 2 id to be attached to repeat unit B.
	 */
	private static final int END_GROUP2_INDEX = 5;

	/**
	 * Constructs an object to undertake block copolymerisation.
	 */
	public BlockCopolymerisation() {
	}

	/**
	 * Constructs an object to undertake block copolymerisation, using the
	 * degree of polymerisation of blocks A & B, the torsion angle between the
	 * blocks (at present not used) and a string representation for end group 1
	 * and 2 and repeat units A and B. Currently restricted to two different
	 * blocks (A & B) only. End groups are optional, if not set the default is
	 * [R][H].
	 * 
	 * @param dPBlockA
	 *            - int representing the number of repeat unit molecules of type
	 *            A joined together before addition of a B unit i.e. AAAAB = 4
	 * @param dPBlockB
	 *            - int representing the number of repeat unit molecules of type
	 *            B joined together before addition of an A unit i.e BBBA = 3
	 * @param torsionAngleValue
	 *            - double, representing the torsion angle between separate
	 *            repeat unit components
	 * @param eg1
	 *            -SMILE string representation of an end group molecule used as
	 *            the starting point in the polymerisation.
	 * @param blockAValue
	 *            - SMILE string representation of repeat unit A
	 * @param blockBValue
	 *            - SMILE string representation of repeat unit B
	 * @param eg2
	 *            -SMILE string representation of an end group molecule used as
	 *            the end point in the polymerisation.
	 * 
	 */
	public BlockCopolymerisation(final int dPBlockA, final int dPBlockB,
			final double torsionAngleValue, final String eg1,
			final String blockAValue, final String blockBValue, final String eg2) {
		if (dPBlockA < 1) {
			throw new IllegalArgumentException(
					"Need to have an integer greater than 1 for the number of units of A!");
		} else {
			this.degreeOfPolymerisationBlockA = dPBlockA;
		}
		if (dPBlockB < 1) {
			throw new IllegalArgumentException(
					"Need to have an integer greater than 1 for the number of units of A!");
		} else {
			this.degreeOfPolymerisationBlockB = dPBlockB;
		}
		if (torsionAngleValue < 0) {
			throw new IllegalArgumentException("Negative torsion angle!");
		} else if (torsionAngleValue > TORSION_ANGLE_UPPER_LIMIT) {
			this.torsionAngle = Math.round(torsionAngleValue
					/ TORSION_ANGLE_UPPER_LIMIT);
		} else {
			this.torsionAngle = torsionAngleValue;
		}

		if (eg1 == null) {
			this.endGroup1 = PolymerisationUtils.convertSmileString2CML(
					"[R][H]", "Eg1");
		} else {
			int rCount = PolymerisationUtils.countNumberofFragmentPoints(eg1,
					"[R]");
			if (rCount == 1) {
				this.endGroup1 = PolymerisationUtils.convertSmileString2CML(
						eg1, "Eg1");
			} else if (rCount != 1) {
				throw new IllegalArgumentException(
						"End groups are only allowed one R group!");
			}
		}

		if (blockAValue == null) {
			throw new NullPointerException(
					"No repeat unit entered, please enter a repeat unit smile string");
		} else {
			int rCount = PolymerisationUtils.countNumberofFragmentPoints(
					blockAValue, "[R]");
			if (rCount == 2) {
				this.blockA = PolymerisationUtils.convertSmileString2CML(
						blockAValue, "RUA");
			} else if (rCount != 2) {
				throw new IllegalArgumentException(
						"Repeat units must have two R-groups!");
			}
		}

		if (blockBValue == null) {
			throw new NullPointerException(
					"No repeat unit entered, please enter a repeat unit smile string");
		} else {
			int rCount = PolymerisationUtils.countNumberofFragmentPoints(
					blockBValue, "[R]");
			if (rCount == 2) {
				this.blockB = PolymerisationUtils.convertSmileString2CML(
						blockBValue, "RUB");
			} else if (rCount != 2) {
				throw new IllegalArgumentException(
						"Repeat units must have two R-groups!");
			}
		}

		if (eg2 == null) {
			this.endGroup2 = PolymerisationUtils.convertSmileString2CML(
					"[R][H]", "Eg2");
		} else {
			int rCount = PolymerisationUtils.countNumberofFragmentPoints(eg2,
					"[R]");
			if (rCount == 1) {
				this.endGroup2 = PolymerisationUtils.convertSmileString2CML(
						eg2, "Eg2");
			} else if (rCount != 1) {
				throw new IllegalArgumentException(
						"End groups are only allowed one R group!");
			}
		}

	}

	/**
	 * Constructs an object to undertake block copolymerisation, using the
	 * degree of polymerisation of blocks A & B, the torsion angle between the
	 * blocks (at present not used) and a CMLMolecule representation for end
	 * group 1 and 2 and repeat units A and B. Currently restricted to two
	 * different blocks (A & B) only. End groups are optional, if not set the
	 * default is [R][H].
	 * 
	 * @param dPBlockA
	 *            - int representing the number of repeat unit molecules of type
	 *            A joined together before addition of a B unit i.e. AAAAB = 4.
	 * @param dPBlockB
	 *            - int representing the number of repeat unit molecules of type
	 *            B joined together before addition of an A unit i.e BBBA = 3.
	 * @param torsionAngleValue
	 *            - double, representing the torsion angle between separate
	 *            repeat unit components
	 * 
	 * @param endGroup1Value
	 *            -CMLMolecule representation of an end group molecule used as
	 *            the starting point in the polymerisation.
	 * @param blockAValue
	 *            - CMLMolecule representation of repeat unit A.
	 * @param blockBValue
	 *            - CMLMolecule representation of repeat unit B.
	 * @param endGroup2Value
	 *            -CMLMolecule representation of an end group molecule used as
	 *            the end point in the polymerisation.
	 * 
	 */
	public BlockCopolymerisation(final int dPBlockA, final int dPBlockB,
			final double torsionAngleValue, final CMLMolecule endGroup1Value,
			final CMLMolecule blockAValue, final CMLMolecule blockBValue,
			final CMLMolecule endGroup2Value) {
		if (dPBlockA < 1) {
			throw new IllegalArgumentException(
					"Need to have an integer greater than 1 for the number of units of A!");
		} else {
			this.degreeOfPolymerisationBlockA = dPBlockA;
		}
		if (dPBlockB < 1) {
			throw new IllegalArgumentException(
					"Need to have an integer greater than 1 for the number of units of A!");
		} else {
			this.degreeOfPolymerisationBlockB = dPBlockB;
		}
		if (torsionAngleValue < 0) {
			throw new IllegalArgumentException("Negative torsion angle!");
		} else if (torsionAngleValue > TORSION_ANGLE_UPPER_LIMIT) {
			this.torsionAngle = Math.round(torsionAngleValue
					/ TORSION_ANGLE_UPPER_LIMIT);
		} else {
			this.torsionAngle = torsionAngleValue;
		}
		if (endGroup1Value == null) {
			this.endGroup1 = PolymerisationUtils.convertSmileString2CML(
					"[R][H]", "Eg1");
		} else {
			if (PolymerisationUtils.getRElements(endGroup1Value).size() == 1) {
				this.endGroup1 = PolymerisationUtils.addId(endGroup1Value,
						"Eg1");
			} else {
				throw new IllegalArgumentException(
						"End groups need to have 1 R group!");
			}
		}
		if (blockAValue == null) {
			throw new NullPointerException(
					"No repeat unit entered, please enter a repeat unit");
		} else {
			if (PolymerisationUtils.getRElements(blockAValue).size() == 2) {
				this.blockA = PolymerisationUtils.addId(blockAValue, "");
			} else {
				throw new IllegalArgumentException(
						"Repeat unit needs two R groups!");
			}
		}
		if (blockBValue == null) {
			throw new NullPointerException(
					"No repeat unit entered, please enter a repeat unit");
		} else {
			if (PolymerisationUtils.getRElements(blockBValue).size() == 2) {
				this.blockB = PolymerisationUtils.addId(blockBValue, "");
			} else {
				throw new IllegalArgumentException(
						"Repeat unit needs two R groups!");
			}
		}
		if (endGroup2Value == null) {
			this.endGroup2 = PolymerisationUtils.convertSmileString2CML(
					"[R][H]", "Eg2");
		} else {
			if (PolymerisationUtils.getRElements(endGroup2Value).size() == 1) {
				this.endGroup2 = PolymerisationUtils.addId(endGroup2Value,
						"Eg2");
			} else {
				throw new IllegalArgumentException(
						"End groups need to have 1 R group!");
			}
		}
	}

	/**
	 * Sets CMLMolecule representation for repeat unit A.
	 * 
	 * @param repeatUnitAValue
	 *            - CMLMolecule representation to set for block A
	 */
	public final void setBlockA(final CMLMolecule repeatUnitAValue) {
		this.blockA = repeatUnitAValue;
	}

	/**
	 * Sets CMLMolecule representation for repeat unit B.
	 * 
	 * @param repeatUnitBValue
	 *            - CMLMolecule representation to set for Block B
	 */
	public final void setBlockB(final CMLMolecule repeatUnitBValue) {
		this.blockB = repeatUnitBValue;
	}

	/**
	 * Sets CMLMolecule representation for end group 1.
	 * 
	 * @param endGroup1Value
	 *            - CMLMolecule representation to set for end group 1
	 */
	public final void setEndGroup1(final CMLMolecule endGroup1Value) {
		this.endGroup1 = endGroup1Value;
	}

	/**
	 * Sets CMLMolecule representation for end group 2.
	 * 
	 * @param endGroup2Value
	 *            - CMLMolecule representation to set for end group 2
	 */
	public final void setEndGroup2(final CMLMolecule endGroup2Value) {
		this.endGroup2 = endGroup2Value;
	}

	/**
	 * Sets the degree of polymerisation for repeat unit A.
	 * 
	 * @param degreeOfPolyA
	 *            - degree of polymerisation of repeat unit A, the number of A
	 *            blocks in the block copolymer (AAAAB) degreeOfPolyA = 4.
	 */
	public final void setDegreeOfPolymerisationA(final int degreeOfPolyA) {
		this.degreeOfPolymerisationBlockA = degreeOfPolyA;
	}

	/**
	 * Sets the degree of polymerisation for repeat unit B.
	 * 
	 * @param degreeOfPolyB
	 *            - degree of polymerisation of repeat unit B, the number of B
	 *            blocks in the block copolymer (ABBBB) degreeOfPolyB = 4.
	 */
	public final void setDegreeOfPolymerisationB(final int degreeOfPolyB) {
		this.degreeOfPolymerisationBlockB = degreeOfPolyB;
	}

	/**
	 * Sets the torsion angle.
	 * 
	 * @param torsionAngleValue
	 *            - double, representing the torsion angle between separate
	 *            repeat unit components.
	 */
	public final void setTorsionAngle(final double torsionAngleValue) {
		this.torsionAngle = torsionAngleValue;
	}

	/**
	 * Returns CMLMolecule representation for repeat unit A.
	 * 
	 * @return blockA
	 */
	public final CMLMolecule getBlockA() {
		return blockA;
	}

	/**
	 * Returns CMLMolecule representation for repeat unit B.
	 * 
	 * @return blockB
	 */
	public final CMLMolecule getBlockB() {
		return blockB;
	}

	/**
	 * Returns CMLMolecule representation for end group 1.
	 * 
	 * @return endGroup1
	 */
	public final CMLMolecule getEndGroup1() {
		return endGroup1;
	}

	/**
	 * Returns CMLMolecule representation for end group 2.
	 * 
	 * @return endGroup2
	 */
	public final CMLMolecule getEndGroup2() {
		return endGroup2;
	}

	/**
	 * Returns int representing the number of repeat unit molecules of type A
	 * joined together before addition of a B unit (AAAAB = 4).
	 * 
	 * @return degreeOfPolymerisationBlockA
	 */
	public final int getDegreeOfPolymerisationA() {
		return degreeOfPolymerisationBlockA;
	}

	/**
	 * Return int representing the number of repeat unit molecules of type B
	 * joined together before addition of an A unit (BBBA = 3).
	 * 
	 * @return degreeOfPolymerisationBlockB
	 */
	public final int getDegreeOfPolymerisationB() {
		return degreeOfPolymerisationBlockB;
	}

	/**
	 * Returns double, representing the torsion angle between separate repeat
	 * unit components.
	 * 
	 * @return torsionAngle
	 */
	public final double getTorsionAngle() {
		return torsionAngle;
	}

	/**
	 * Returns XML String representation of the built block copolymer in 2D.
	 * 
	 * @return blockCopolymerMolecule.toXML() (non-Javadoc)
	 * @see org.xmlcml.cml.tools.polymer.Polymerisation#generatePML()
	 */
	public final String generatePML() {
		return blockCopolymerMolecule.toXML();
	}

	/**
	 * . Joins up a list of CMLMolecules, given the atom ids to create joining
	 * bonds to build polymer
	 * 
	 * @param molecules
	 *            - ArrayList<CMLMolecules> to be joined together by bonds
	 * @param ids
	 *            - ArrayList<String> identifying the atoms used to bond the
	 *            separate molecules together in the molecules list
	 */
	private void joinMolecules(final ArrayList<CMLMolecule> molecules,
			final ArrayList<String> ids) {
		blockCopolymerMolecule = new CMLMolecule();
		for (int i = 0; i < molecules.size(); i++) {
			// Add the atoms
			PolymerisationUtils.addAtoms(molecules, i, blockCopolymerMolecule);
			// Now add the bonds
			PolymerisationUtils.addBonds(molecules, i, blockCopolymerMolecule);
		}
		// Need to add the linker bonds to the molecule now.
		CMLBond cb = new CMLBond();
		cb.setAttribute("atomRefs2", ids.get(END_GROUP1_INDEX) + " "
				+ ids.get(REPEAT_UNITA_INDEX_START));
		cb.setId(ids.get(END_GROUP1_INDEX) + " "
				+ ids.get(REPEAT_UNITA_INDEX_START));
		cb.setOrder("1");
		blockCopolymerMolecule.addBond(cb);
		CMLBond cb1 = new CMLBond();
		cb1.setAttribute("atomRefs2", ids.get(REPEAT_UNITA_INDEX_END) + " "
				+ ids.get(REPEAT_UNITB_INDEX_START));
		cb1.setId(ids.get(REPEAT_UNITA_INDEX_END) + " "
				+ ids.get(REPEAT_UNITB_INDEX_START));
		cb1.setOrder("1");
		blockCopolymerMolecule.addBond(cb1);
		CMLBond cb2 = new CMLBond();
		cb2.setAttribute("atomRefs2", ids.get(REPEAT_UNITB_INDEX_END) + " "
				+ ids.get(END_GROUP2_INDEX));
		cb2.setId(ids.get(REPEAT_UNITB_INDEX_END) + " "
				+ ids.get(END_GROUP2_INDEX));
		cb2.setOrder("1");
		blockCopolymerMolecule.addBond(cb2);
		// Add R-R delete method
		ArrayList<CMLBond> bothRAtoms = new ArrayList<CMLBond>();
		CMLBondArray rests = blockCopolymerMolecule.getBondArray();
		PolymerisationUtils.createNewMoleculeJoinedWithoutRRgroups(rests,
				bothRAtoms, blockCopolymerMolecule);
	}

	/**
	 * Adds 3D coordinates to an existing 2D XML representation of the built
	 * polymer.
	 * 
	 * @param pmlStringNoCoordinates
	 *            - 2D XML representation of the built polymer
	 * @return XML string representation of the polymer with 3D coordinates.
	 *         Coordinate generation is done using an external dependencies,
	 *         from the Chemistry Development kit ModelBuilder3D class @link
	 *         (http
	 *         ://pele.farmbio.uu.se/nightly/api/org/openscience/cdk/modeling
	 *         /builder3d/ModelBuilder3D.html).
	 * @throws Exception
	 *             if 3D coordinate generation fails.
	 */
	public final String add3DCoordinates(final String pmlStringNoCoordinates)
			throws Exception {
		String molWithCoords = PolymerisationUtils
				.add3DCoordinates(pmlStringNoCoordinates);
		return molWithCoords;
	}

	/*
	 * public static void main(String[] args) throws Exception {
	 * BlockCopolymerisation blockCopolymerisation = new
	 * BlockCopolymerisation(5,
	 * 2,45.0,"Cl[R]","[R]c1cccc(C[R])c1","[R]OCC1CCCC(C[R])C1","CCO[R]");
	 * CMLFragment frag = blockCopolymerisation.getFragment(); Document
	 * pmlBasicDoc = PolymerisationUtils.createPMLBasic(frag); Document
	 * pmlIntermediateDoc = PolymerisationUtils.createPMLIntermediate(frag);
	 * System.out.println(pmlBasicDoc.toXML());
	 * System.out.println(pmlIntermediateDoc.toXML()); } / Document pmlBasicDoc
	 * = blockCopolymerisation.createPMLBasic(); Document pmlIntermediateDoc =
	 * blockCopolymerisation.createPMLIntermediate();
	 * System.out.println(pmlBasicDoc.toXML());
	 * System.out.println(pmlIntermediateDoc.toXML()); //String result =
	 * blockCopolymerisation.getPolymer().toXML();
	 * //blockCopolymerisation.add3DCoordinates(result); }
	 */

	/**
	 * Returns CMLMolecule representation of the built polymer.
	 * 
	 * @return blockCopolymerMolecule (non-Javadoc)
	 * @see org.xmlcml.cml.tools.polymer.Polymerisation#getPolymer()
	 */
	public final CMLMolecule getPolymer() {
		//boolean buildable = PolymerisationUtils.isBuildable(getFragment());
		boolean buildable = true;
		if (buildable == true) {
			CMLJoin eg1BlockA = new CMLJoin();
			ArrayList<CMLAtom> eg1R = PolymerisationUtils
					.getRElements(endGroup1);
			ArrayList<CMLAtom> eg2R = PolymerisationUtils
					.getRElements(endGroup2);
			ArrayList<CMLAtom> repeatUnitA;
			ArrayList<CMLAtom> repeatUnitB;
			if (this.degreeOfPolymerisationBlockA == 1) {
				repeatUnitA = PolymerisationUtils.getRElements(blockA);
			} else {
				blockA = PolymerisationUtils.countExpression(
						this.degreeOfPolymerisationBlockA, blockA, "RUA");
				repeatUnitA = PolymerisationUtils.getRElements(blockA);
			}
			if (this.degreeOfPolymerisationBlockB == 1) {
				repeatUnitB = PolymerisationUtils.getRElements(blockB);
			} else {
				blockB = PolymerisationUtils.countExpression(
						this.degreeOfPolymerisationBlockB, blockB, "RUB");
				repeatUnitB = PolymerisationUtils.getRElements(blockB);
			}
			ArrayList<CMLMolecule> blockAandB = new ArrayList<CMLMolecule>();
			blockAandB.add(blockA);
			blockAandB.add(blockB);
			String[] eg1JoinValues = new String[2];
			eg1JoinValues[0] = eg1R.get(0).getId();
			eg1JoinValues[1] = repeatUnitA.get(0).getId();
			eg1R.get(0).getId();
			eg1BlockA.setAtomRefs2(eg1JoinValues);
			eg1BlockA.processMoleculeRefs2AndAtomRefs2(endGroup1, blockAandB
					.get(0));
			ArrayList<CMLMolecule> molecules4Joining = new ArrayList<CMLMolecule>();
			molecules4Joining.add(endGroup1);
			molecules4Joining.add(blockAandB.get(0));
			molecules4Joining.add(blockAandB.get(1));
			molecules4Joining.add(endGroup2);
			ArrayList<String> ids4Joining = new ArrayList<String>();
			ids4Joining.add(eg1R.get(0).getId());
			ids4Joining.add(repeatUnitA.get(0).getId());
			ids4Joining.add(repeatUnitA.get(1).getId());
			ids4Joining.add(repeatUnitB.get(0).getId());
			ids4Joining.add(repeatUnitB.get(1).getId());
			ids4Joining.add(eg2R.get(0).getId());
			joinMolecules(molecules4Joining, ids4Joining);
			return blockCopolymerMolecule;
		} else {
			UnbuildableException unbuildableException = new UnbuildableException(
					"Can't be built");
			unbuildableException.getMessage();
			return null;
		}
	}

	/**
	 * Method returns a fully atomistic CML Document representation of the
	 * polymer.
	 * 
	 * @return Document
	 */
	public final Document createCML() {
		Builder builder = new Builder();
		Document doc = null;
		try {
			doc = builder.build(blockCopolymerMolecule.toXML(), "");
		} catch (ValidityException e) {
			e.printStackTrace();
		} catch (ParsingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return doc;
	}

	/**
	 * Generates a CMLFragment for PML document generation.
	 * 
	 * @return CMLFragment
	 */
	public final CMLFragment getFragment() {
		ArrayList<CMLAtom> eg1R = PolymerisationUtils.getRElements(endGroup1);
		ArrayList<CMLAtom> eg2R = PolymerisationUtils.getRElements(endGroup2);
		ArrayList<CMLMolecule> repeatUnitAMolecules = PolymerisationUtils
				.getRepeatUnitMolecules(degreeOfPolymerisationBlockA, blockA,
						"RUA");
		ArrayList<CMLMolecule> repeatUnitBMolecules = PolymerisationUtils
				.getRepeatUnitMolecules(degreeOfPolymerisationBlockB, blockB,
						"RUB");
		ArrayList<ArrayList<String>> rgroupRUAIds = PolymerisationUtils
				.getRgroupConnectors(repeatUnitAMolecules);
		ArrayList<ArrayList<String>> rgroupRUBIds = PolymerisationUtils
				.getRgroupConnectors(repeatUnitBMolecules);
		// End group 1
		CMLMolecule endGroup1Blank = new CMLMolecule();
		endGroup1Blank.setRef("Eg1");
		CMLFragment endGroup1BlankFragment = new CMLFragment();
		endGroup1BlankFragment.addMolecule(endGroup1Blank);
		CMLFragment endGroup2BlankFragment = new CMLFragment();
		CMLFragment parentFragment = new CMLFragment();

		// Repeat unit A
		ArrayList<CMLMolecule> blankRepeatUnitsA = new ArrayList<CMLMolecule>();
		ArrayList<CMLFragment> blankFragmentsA = new ArrayList<CMLFragment>();
		// Repeat unit B
		ArrayList<CMLMolecule> blankRepeatUnitsB = new ArrayList<CMLMolecule>();
		ArrayList<CMLFragment> blankFragmentsB = new ArrayList<CMLFragment>();
		PolymerisationUtils.addBlankFragments(repeatUnitAMolecules,
				blankRepeatUnitsA, repeatUnitBMolecules, blankRepeatUnitsB,
				blankFragmentsA, blankFragmentsB);
		// End group 2
		CMLMolecule endGroup2Blank = new CMLMolecule();
		endGroup2Blank.setRef("Eg2");
		endGroup2BlankFragment.addMolecule(endGroup2Blank);
		// Manually add join between end group 1 and repeatUnit(0)
		CMLJoin endGroup1ToRepeatUnit = new CMLJoin();
		String[] eg1JoinValues = new String[2];
		eg1JoinValues[0] = eg1R.get(0).getId();
		eg1JoinValues[1] = rgroupRUAIds.get(0).get(0);
		endGroup1ToRepeatUnit.setAtomRefs2(eg1JoinValues);
		String moleculeRefs = "PREVIOUS NEXT";
		endGroup1ToRepeatUnit.processMoleculeRefs2AndAtomRefs2(endGroup1Blank,
				repeatUnitAMolecules.get(0));
		endGroup1ToRepeatUnit.setMoleculeRefs2(moleculeRefs);
		parentFragment.appendChild(endGroup1BlankFragment);
		parentFragment.addJoin(endGroup1ToRepeatUnit);
		parentFragment.appendChild(blankFragmentsA.get(0));
		// Loop through and create fragment joins and add fragments to
		// parentFragment
		if (repeatUnitAMolecules.size() > 1) {
			for (int i = 0; i < repeatUnitAMolecules.size(); i++) {
				if (i == 0) {
					CMLJoin ru0toru1 = new CMLJoin();
					String[] ru0Ru1 = new String[2];
					ru0Ru1[0] = rgroupRUAIds.get(0).get(1);
					ru0Ru1[1] = rgroupRUAIds.get(1).get(0);
					ru0toru1.setAtomRefs2(ru0Ru1);
					ru0toru1.processMoleculeRefs2AndAtomRefs2(
							repeatUnitAMolecules.get(i), repeatUnitAMolecules
									.get(i + 1));
					ru0toru1.setMoleculeRefs2(moleculeRefs);
					parentFragment.addJoin(ru0toru1);
					parentFragment.appendChild(blankFragmentsA.get(i + 1));
				}
				if (i > 0 && i < repeatUnitAMolecules.size() - 1) {
					CMLJoin repeatUnit2RepeatUnit = new CMLJoin();
					repeatUnit2RepeatUnit.setAtomRefs2(rgroupRUAIds.get(i).get(
							1)
							+ " " + rgroupRUAIds.get(i + 1).get(0));
					repeatUnit2RepeatUnit.processMoleculeRefs2AndAtomRefs2(
							repeatUnitAMolecules.get(i), repeatUnitAMolecules
									.get(i + 1));
					repeatUnit2RepeatUnit.setMoleculeRefs2(moleculeRefs);
					parentFragment.addJoin(repeatUnit2RepeatUnit);
					parentFragment.appendChild(blankFragmentsA.get(i + 1));
				}
			}
		}
		// Manually add repeat unit A-B join
		CMLJoin repeatUnitAToB = new CMLJoin();
		repeatUnitAToB.setAtomRefs2(rgroupRUAIds.get(rgroupRUAIds.size() - 1)
				.get(1)
				+ " " + rgroupRUBIds.get(0).get(0));
		repeatUnitAToB.processMoleculeRefs2AndAtomRefs2(repeatUnitAMolecules
				.get(repeatUnitAMolecules.size() - 1), repeatUnitBMolecules
				.get(0));
		repeatUnitAToB.setMoleculeRefs2(moleculeRefs);
		parentFragment.addJoin(repeatUnitAToB);
		parentFragment.appendChild(blankFragmentsB.get(0));
		// Add all of repeat unit B
		if (repeatUnitBMolecules.size() > 1) {
			for (int i = 0; i < repeatUnitBMolecules.size(); i++) {
				if (i == 0) {
					CMLJoin ru0toru1 = new CMLJoin();
					String[] ru0Ru1 = new String[2];
					ru0Ru1[0] = rgroupRUBIds.get(0).get(1);
					ru0Ru1[1] = rgroupRUBIds.get(1).get(0);
					ru0toru1.setAtomRefs2(ru0Ru1);
					ru0toru1.processMoleculeRefs2AndAtomRefs2(
							repeatUnitBMolecules.get(i), repeatUnitBMolecules
									.get(i + 1));
					ru0toru1.setMoleculeRefs2(moleculeRefs);
					parentFragment.addJoin(ru0toru1);
					parentFragment.appendChild(blankFragmentsB.get(i + 1));
				}
				if (i > 0 && i < repeatUnitBMolecules.size() - 1) {
					CMLJoin repeatUnit2RepeatUnit = new CMLJoin();
					repeatUnit2RepeatUnit.setAtomRefs2(rgroupRUBIds.get(i).get(
							1)
							+ " " + rgroupRUBIds.get(i + 1).get(0));
					repeatUnit2RepeatUnit.processMoleculeRefs2AndAtomRefs2(
							repeatUnitBMolecules.get(i), repeatUnitBMolecules
									.get(i + 1));
					repeatUnit2RepeatUnit.setMoleculeRefs2(moleculeRefs);
					parentFragment.addJoin(repeatUnit2RepeatUnit);
					parentFragment.appendChild(blankFragmentsB.get(i + 1));
				}
			}
		}
		// Add end group 2
		CMLJoin lastRepeatUnitToEndGroup = new CMLJoin();
		lastRepeatUnitToEndGroup.setAtomRefs2(rgroupRUBIds.get(
				rgroupRUBIds.size() - 1).get(1)
				+ " " + eg2R.get(0).getId());
		lastRepeatUnitToEndGroup.processMoleculeRefs2AndAtomRefs2(
				repeatUnitBMolecules.get(repeatUnitBMolecules.size() - 1),
				endGroup2Blank);
		lastRepeatUnitToEndGroup.setMoleculeRefs2(moleculeRefs);
		parentFragment.appendChild(blankFragmentsB
				.get(blankFragmentsB.size() - 1));
		parentFragment.addJoin(lastRepeatUnitToEndGroup);
		parentFragment.appendChild(endGroup2BlankFragment);
		return parentFragment;
	}

}
